<?php

/**
 * Contains a class to display /install.php.
 *
 * PHP Version 5
 *
 * @category BuyLocal
 * @package  BuyLocal
 * @author   Alexander Adami <adamia@rpi.edu>
 * @author   Kevin Fodness <kevin.fodness@gmail.com>
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
 * @link     http://code.google.com/p/buylocalopensourcerensselaer/
 */
 
/** @uses makeHash To make a standardized hash of a string. */
require_once 'function_makehash.php';

/** @uses printError To print error messages to the screen. */
require_once 'function_printerror.php';

/** @uses validEmail To check for email validity. */
require_once 'function_validemail.php';

/** @uses valueOf For safe handling of arrays. */
require_once 'function_valueof.php';

/**
 * A class to display /install.php.
 *
 * @category BuyLocal
 * @package  BuyLocal
 * @author   Alexander Adami <adamia@rpi.edu>
 * @author   Kevin Fodness <kevin.fodness@gmail.com>
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
 * @link     http://code.google.com/p/buylocalopensourcerensselaer/
 */ 
class InstallPage
{
    /**
     * Variable to contain the confirmation code from POST.
     *
     * @access public
     * @var string
     */
    public $confCode;
    
    /**
     * Variable to contain the administrator's email from POST.
     *
     * @access public
     * @var string
     */
    public $admEmail;
    
    /**
     * Variable to contain the administrator's password from POST.
     *
     * @access public
     * @var string
     */
    public $admPwd;
    
    /**
     * Variable to contain the source database server from POST.
     *
     * @access public
     * @var string
     */
    public $srcServer;
    
    /**
     * Variable to contain the source database username from POST.
     *
     * @access public
     * @var string
     */
    public $srcUn;
    
    /**
     * Variable to contain the source database password from POST.
     *
     * @access public
     * @var string
     */
    public $srcPwd;
    
    /**
     * Variable to contain the source database name from POST.
     *
     * @access public
     * @var string
     */
    public $srcDb;
    
    /**
     * Variable to contain a connection to the old database.
     *
     * @access public
     * @var object
     */
    public $oldDb;
    
    /**
     * State variable controlling whether to print the form.
     *
     * @access public
     * @var bool
     */
    public $printForm;
    
    /**
     * State variable controlling whether to process the form.
     *
     * @access public
     * @var bool
     */
    public $processForm;
    
    /**
     * State variable controlling whether to build the database.
     *
     * @access public
     * @var bool
     */
    public $buildDatabase;
    
    /**
     * State variable controlling whether to import data from a 0.1.0 database.
     *
     * @access public
     * @var bool
     */
    public $importFromOld;
    
    /**
     * Variable containing any error messages that need to be printed.
     *
     * @access public
     * @var string
     */
    public $error;
    
    /**
     * Constructor function - establishes a database connection and determines state.
     *
     * @access public
     * @return null
     */
    public function __construct()
    {
        /** Determines if the passcode is set in presets.php. */
        if (INSTALL_PASSWORD == '') {
            $this->error = 'You must provide an installation password in the '
                . 'presets file in order to proceed.';
        }
        
        /** Establishes a database connection. */
        $this->db = new Database(DBSERVER, DBUSER, DBPASS, DBNAME);
        
        /** Sets local values from POST values. */
        $this->confCode  = valueOf($_POST, 'conf_code');
        $this->admEmail  = valueOf($_POST, 'adm_email');
        $this->admPwd    = valueOf($_POST, 'adm_pwd');
        $this->srcServer = valueOf($_POST, 'src_server');
        $this->srcUn     = valueOf($_POST, 'src_un');
        $this->srcPwd    = valueOf($_POST, 'src_pwd');
        $this->srcDb     = valueOf($_POST, 'src_db');
        
        /** Determines whether to print the form or process the form. */
        if (count($_POST) == 0) {
            $this->printForm = true;
            $this->processForm = false;
        } else {
            $this->printForm = false;
            $this->processForm = true;
        }
    }
    
    /**
     * Function to print the page header.
     *
     * @access private
     * @return null
     */
    private function _printHeader()
    {
        echo '<!DOCTYPE html>', "\n\n",
            '<html>', "\n\n",
            '<head>', "\n",
            ' <meta http-equiv="Content-Type" content="text/html;',
            'charset=UTF-8" />', "\n",
            ' <title>', SITENAME, ': Install</title>', "\n",
            ' <link rel="stylesheet" type="text/css" ',
            'href="', RELATIVE_PATH, 'stylesheets/main.css" />', "\n",
            '</head>', "\n\n",
            '<body onload="document.getElementById(\'conf_code\').focus()">', "\n\n",
            '<h1 class="centered">', SITENAME, ' Installation</h1>', "\n";
    }
    
    /**
     * Function to print the installation parameters form.
     *
     * @access private
     * @return null
     */
    private function _printForm()
    {
        echo '<form id="installation" method="post" action="">', "\n",
            ' <div>', "\n",
            '  <label for="conf_code">Installation Password:</label>', "\n",
            '  <input id="conf_code" name="conf_code" type="password" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <label for="adm_email">Administrator Email:</label>', "\n",
            '  <input id="adm_email" name="adm_email" type="text" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <label for="adm_pwd">Administrator Password:</label>', "\n",
            '  <input id="adm_pwd" name="adm_pwd" type="password" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <h2>Migrate &amp; Upgrade</h2>', "\n",
            '  <p>Fill out the next section if you are importing data from an ',
            'existing BuyLocal 0.1.0 installation.</p>', "\n",
            '  <p><strong>This database must be different than the database ',
            'specified in the presets file.</strong></p>', "\n",
            '</div>', "\n",
            ' <div>', "\n",
            '  <label for="src_server">Source Server:</label>', "\n",
            '  <input id="src_server" name="src_server" type="text" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <label for="src_un">Source Username:</label>', "\n",
            '  <input id="src_un" name="src_un" type="text" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <label for="src_pwd">Source Password:</label>', "\n",
            '  <input id="src_pwd" name="src_pwd" type="password" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <label for="src_db">Source Database:</label>', "\n",
            '  <input id="src_db" name="src_db" type="text" />', "\n",
            ' </div>', "\n",
            ' <div>', "\n",
            '  <input type="submit" value="Install" />', "\n",
            ' </div>', "\n",
            '</form>', "\n";
    }
    
    /**
     * Function to print the page footer.
     *
     * @access private
     * @return null
     */
    private function _printFooter()
    {
        echo "\n", '</body>', "\n\n", '</html>';
    }
    
    /**
     * Function to process the form and set flags.
     *
     * @access private
     * @return bool    True on success, false on failure.
     */
    private function _processForm()
    {
        /** Checks to see if confirmation code matches. */
        if ($this->confCode != INSTALL_PASSWORD) {
            $this->error = 'The installation password you provided does not match '
                . 'the installation password in the presets file.';
            return false;
        }
        
        /** Checks to see if the administrator email and password is present. */
        if (!validEmail($this->admEmail) || strlen($this->admPwd) == 0) {
            $this->error = 'You must provide a valid email address and password '
                . 'for the administrator account in order to proceed.';
            return false;
        }
        
        /** Checks to see whether to import from an old database. */
        if (strlen($this->srcServer) > 0
            && strlen($this->srcUn) > 0
            && strlen($this->srcPwd) > 0
            && strlen($this->srcDb) > 0
        ) {
            $this->importFromOld = true;
            return true;
        }
        
        /** Checks to see whether migration information is incomplete. */
        if (strlen($this->srcServer) > 0
            || strlen($this->srcUn) > 0
            || strlen($this->srcPwd) > 0
            || strlen($this->srcDb) > 0
        ) {
            $this->error = 'In order to import from an existing database, you '
                . 'must specify the server, username, password, and database.';
            return false;
        }
        
        return true;
    }
    
    /**
     * Function to print the finished message.
     *
     * @param int &$begin The UNIX timestamp for when the operation began.
     *
     * @access private
     * @return null
     */
    private function _printDone(&$begin)
    {
        $current = valueOf(getdate(), 0);
        echo 'Done (', ($current - $begin), ' seconds)</p>', "\n\n";
        $begin = $current;
    }
    
    /**
     * Function to build the database.
     *
     * @access private
     * @return null
     */
    private function _buildDatabase()
    {
        $user = new User(null, 'Administrator', $this->admEmail, new Zip(D_ZIP),
            R_ADMIN);
        $password = $this->admPwd;
        $start = valueOf(getdate(), 0);
        $begin = $start;
        echo ' <p>Dropping tables ... ';
        $this->db->loadFileEach('drop_tables.sql');
        $this->_printDone($begin);
        echo ' <p>Dropping functions ... ';
        $this->db->loadFile('delete_functions.sql');
        $this->_printDone($begin);
        echo ' <p>Creating tables ... ';
        $this->db->loadFileEach('create_tables.sql');
        $this->_printDone($begin);
        echo ' <p>Creating functions ... ';
        $this->db->loadFile('create_functions.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>roles</strong> ... ';
        $this->db->loadFile('populate_roles.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>categories</strong> ... ';
        $this->db->loadFile('populate_categories.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>countries</strong> ... ';
        $this->db->loadFile('populate_countries.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>productconditions</strong> ... ';
        $this->db->loadFile('populate_productconditions.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>flagtypes</strong> ... ';
        $this->db->loadFile('populate_flagtypes.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>zipcodes</strong> ... ';
        $this->db->loadFileEach('populate_zipcodes.sql');
        $this->_printDone($begin);
        echo ' <p>Populating <strong>content</strong> ... ';
        $this->db->loadFile('populate_content.sql');
        $this->_printDone($begin);
        echo ' <p>Creating default administrator ... ';
        $this->error = $this->db->addUser($user, $password);
        $this->_printDone($begin);
        if ($this->importFromOld) {
            $this->_importFromOld($begin);
        }
        echo ' <p>Finished. Installation took ', (valueOf(getdate(), 0) - $start),
            ' seconds.</p>', "\n",
            '<p><strong>Check the inbox of ', $user->email, ' for an activation ',
            'email.  You will need to  activate the administrative account prior ',
            'to using it.</strong></p>', "\n";
        printError('WARNING: Delete the install.php file now.');
        echo ' <p>The installation file represents a security risk if not deleted. ',
            'Refer to the README.html file for post-installation instructions.</p>',
            "\n";
        if ($this->error == 1) {
            $this->error = null;
        }
    }
    
    /**
     * Function to import data from a 0.1.0 database into the new database.
     *
     * @param int &$begin The time counter for operations.
     *
     * @access private
     * @return null
     */
    private function _importFromOld(&$begin)
    {
        /** Establishes a connection to the old database to extract data. */
        $this->oldDb = new mysqli($this->srcServer, $this->srcUn, $this->srcPwd, 
            $this->srcDb);
        
        /** Calls the copy functions. */
        echo '<p>Populating <strong>users, userphones, usernames, ',
            'useractivation</strong> ... ';
        $this->_copyUsers();
        $this->_printDone($begin);
        echo '<p>Populating <strong>stores, storephones, storefaxes, storeemails, ',
            'storecategories, storeowners, storeimages</strong> ... ';
        $this->_copyStores();
        $this->_printDone($begin);
    }
    
    /**
     * Function to determine if a userid is a store owner.
     *
     * @param int $userid The userid to look up.
     *
     * @access private
     * @return null
     */
    private function _isStoreOwner($userid)
    {
        $sql = 'SELECT COUNT(`s_id`) AS `count` '
            . 'FROM `store_owners` '
            . 'WHERE `u_id` = \'' . $userid . '\' '
            . 'LIMIT 0,1;';
        $this->oldDb->real_query($sql)
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $result = $this->oldDb->store_result()
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $obj    = $result->fetch_object()
            or trigger_error($result->error, E_USER_ERROR);
        return $obj->count;
    }

    /**
     * Function to copy the users tables.
     *
     * @access private
     * @return null
     */
    private function _copyUsers()
    {
        $insertU = false;
        $insertP = false;
        $insertN = false;
        $insertA = false;
    
        $sqlU = 'INSERT INTO `users` (`userid`, `roleid`, `password`, `salt`, '
            . '`email`, `zip`, `isactive`, `registeredon`, `lastlogon`) VALUES';
        $sqlP = 'INSERT INTO `userphones` '
            . '(`userid`, `phone`, `description`) VALUES';
        $sqlN = 'INSERT INTO `usernames` (`userid`, `name`) VALUES';
        $sqlA = 'INSERT INTO `useractivation` (`userid`, `code`) VALUES';
        $sql  = 'SELECT `u_id`, `u_email`, `u_pass`, `u_zip`, `u_reg_date`, '
            . '`u_admin`, `u_phone`, `u_name`, `u_salt`, `u_active`, '
            . '`u_activatecode` '
            . 'FROM `users` '
            . 'ORDER BY `u_id` ASC';
    
        $this->oldDb->real_query($sql)
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $result = $this->oldDb->store_result()
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        while ($obj = $result->fetch_object()) {
            if ($obj->u_admin) {
                $role = 3;
            } elseif ($this->_isStoreOwner($obj->u_id)) {
                $role = 2;
            } else {
                $role = 1;
            }
            if ($obj->u_phone != '') {
                $phone   = str_replace('-', '', $obj->u_phone);
                $phone   = substr($phone, 0, 3) . '-'
                    . substr($phone, 3, 3) . '-'
                    . substr($phone, 6, 4);
                $sqlP   .= ' (\'' . $obj->u_id . '\', '
                    . '\'' . $phone . '\', '
                    . '\'Main\'),';
                $insertP = true;
            }
            if ($obj->u_name != '') {
                $sqlN   .= ' (\'' . $obj->u_id . '\', '
                    . '\'' . $obj->u_name . '\'),';
                $insertN = true;
            }
            if ($obj->u_active == 0 && $obj->u_activatecode != '') {
                $sqlA   .= ' (\'' . $obj->u_id . '\', '
                    . '\'' . $obj->u_activatecode . '\'),';
                $insertA = true;
            }
            $sqlU   .= ' (\'' . $obj->u_id . '\', '
                . '\'' . $role . '\', '
                . '\'' . $obj->u_pass . '\', '
                . '\'' . $obj->u_salt . '\', '
                . '\'' . $obj->u_email . '\', '
                . '\'' . $obj->u_zip . '\', '
                . '\'' . $obj->u_active . '\', '
                . '\'' . $obj->u_reg_date . '\', '
                . '\'0\'),';
            $insertU = true;
        }
        $result->close();
    
        if ($insertU) {
            $this->db->loadQuery(substr($sqlU, 0, -1));
        }
        if ($insertP) {
            $this->db->loadQuery(substr($sqlP, 0, -1));
        }
        if ($insertN) {
            $this->db->loadQuery(substr($sqlN, 0, -1));
        }
        if ($insertA) {
            $this->db->loadQuery(substr($sqlA, 0, -1));
        }
    }
    
    /**
     * Function to copy the stores tables.
     *
     * @access private
     * @return null
     */
    private function _copyStores()
    {
        $sqlS = 'INSERT INTO `stores` (`storeid`, `name`, `description`, '
            . '`dateadded`, `address`, `city`, `state`, `zip`, `countryid`, '
            . '`latitude`, `longitude`) VALUES';
        $sqlP = 'INSERT INTO `storephones` (`storeid`, `phone`, `description`) '
            . 'VALUES';
        $sqlF = 'INSERT INTO `storefaxes` (`storeid`, `fax`, `description`) VALUES';
        $sqlE = 'INSERT INTO `storeemails` (`storeid`, `email`, `description`) '
            . 'VALUES';
        $sqlC = 'INSERT INTO `storecategories` (`storeid`, `categoryid`) VALUES';
        $sqlO = 'INSERT INTO `storeowners` (`storeid`, `userid`) VALUES';
        $sqlI = 'INSERT INTO `storeimages` (`storeid`, `extension`) VALUES';
        
        $insertS = false;
        $insertP = false;
        $insertF = false;
        $insertE = false;
        $insertC = false;
        $insertO = false;
        $insertI = false;
        
        $sql = 'SELECT `s_id`, `s_name`, `s_date_created`, `s_address`, `s_city`, '
            . '`s_state`, `s_zip`, `s_phone`, `s_fax`, `s_description`, `s_email`, '
            . '`s_lng`, `s_lat`, `s_picture` '
            . 'FROM `stores` '
            . 'ORDER BY `s_id` ASC;';
        $this->oldDb->real_query($sql)
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $result = $this->oldDb->store_result()
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        while ($obj = $result->fetch_object()) {
            $obj->s_name = stripslashes($obj->s_name);
            $sqlS   .= ' (\'' . $obj->s_id . '\', '
                . '\'' . $this->oldDb->real_escape_string($obj->s_name) . '\', '
                . '\'' . $this->oldDb->real_escape_string($obj->s_description)
                . '\', '
                . '\'' . $obj->s_date_created . '\', '
                . '\'' . $this->oldDb->real_escape_string($obj->s_address) . '\', '
                . '\'' . $this->oldDb->real_escape_string($obj->s_city) . '\', '
                . '\'' . $obj->s_state . '\', '
                . '\'' . $obj->s_zip . '\', '
                . '\'199\', '
                . '\'' . $obj->s_lat . '\', '
                . '\'' . $obj->s_lng . '\'),';
            $insertS = true;
            if ($obj->s_phone != '') {
                $phone   = str_replace('-', '', $obj->s_phone);
                $phone   = substr($phone, 0, 3) . '-'
                    . substr($phone, 3, 3) . '-'
                    . substr($phone, 6, 4);
                $sqlP   .= ' (\'' . $obj->s_id . '\', '
                    . '\'' . $phone . '\', '
                    . '\'Main\'),';
                $insertP = true;
            }
            if ($obj->s_fax != '') {
                $fax     = str_replace('-', '', $obj->s_fax);
                $fax     = substr($fax, 0, 3) . '-'
                    . substr($fax, 3, 3) . '-'
                    . substr($fax, 6, 4);
                $sqlF   .= ' (\'' . $obj->s_id . '\', '
                    . '\'' . $fax . '\', '
                    . '\'Main\'),';
                $insertF = true;
            }
            if ($obj->s_email != '') {
                $sqlE   .= ' (\'' . $obj->s_id . '\', '
                    . '\'' . $obj->s_email . '\', '
                    . '\'Main\'),';
                $insertE = true;
            }
            if ($obj->s_picture != '') {
                $sqlI   .= ' (\'' . $obj->s_id . '\', '
                    . '\'' . $obj->s_picture . '\'),';
                $insertI = true;
            }
        }
        $result->close();
    
        $categories     = array();
        $categories[1]  = 1;
        $categories[32] = 12;
        $categories[34] = 13;
        $categories[35] = 14;
        $categories[36] = 2;
        $categories[48] = 3;
        $categories[52] = 4;
        $categories[55] = 5;
        $categories[56] = 6;
        $categories[57] = 7;
        $categories[58] = 8;
        $categories[59] = 9;
        $categories[62] = 10;
        $categories[64] = 11;
        $categories[65] = 15;
        
        $sql = 'SELECT `s_id`, `c_id` FROM `stores_categories`;';
        $this->oldDb->real_query($sql)
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $result = $this->oldDb->store_result()
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        while ($obj = $result->fetch_object()) {
            $sqlC   .= ' (\'' . $obj->s_id . '\', '
                . '\'' . $categories[$obj->c_id] . '\'),';
            $insertC = true;
        }
        $result->close();
        
        $sql = 'SELECT `s_id`, `u_id` FROM `store_owners`;';
        $this->oldDb->real_query($sql)
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        $result = $this->oldDb->store_result()
            or trigger_error($this->oldDb->error, E_USER_ERROR);
        while ($obj = $result->fetch_object()) {
            $sqlO   .= ' (\'' . $obj->s_id . '\', \'' . $obj->u_id . '\'),';
            $insertO = true;
        }
        $result->close();
        
        if ($insertS) {
            $this->db->loadQuery(substr($sqlS, 0, -1));
        }
        if ($insertP) {
            $this->db->loadQuery(substr($sqlP, 0, -1));
        }
        if ($insertF) {
            $this->db->loadQuery(substr($sqlF, 0, -1));
        }
        if ($insertE) {
            $this->db->loadQuery(substr($sqlE, 0, -1));
        }
        if ($insertC) {
            $this->db->loadQuery(substr($sqlC, 0, -1));
        }
        if ($insertO) {
            $this->db->loadQuery(substr($sqlO, 0, -1));
        }
        if ($insertI) {
            $this->db->loadQuery(substr($sqlI, 0, -1));
        }
    }
    
    /**
     * Function to display the install page based on page state.
     *
     * @access public
     * @return null
     */
    public function display()
    {
        $this->_printHeader();
        if (!$this->printForm && strlen($this->error) == 0) {
            if ($this->_processForm()) {
                $this->_buildDatabase();
            }
        }
        if ($this->error) {
            echo '<div class="centered">';
            printError($this->error);
            echo '</div>';
            $this->printForm = true;
        }
        if ($this->printForm) {
            $this->_printForm();
        }
        $this->_printFooter();
    }
}

?>